package com.h819.core.utils;

import info.monitorenter.cpdetector.io.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.MalformedURLException;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//重新参考 commons-lang

/**
 * @ClassName: CharSetUtils
 * @Description: TODO(编码工具类)
 * @author h819
 * @date Dec 6, 2009 12:46:38 PM
 * @version V1.0
 */
public class CharSetUtils {

	/** 7位ASCII字符，也叫作ISO646-US、Unicode字符集的基本拉丁块 */
	public static final String US_ASCII = "US-ASCII";

	/** ISO 拉丁字母表 No.1，也叫作 ISO-LATIN-1 */
	public static final String ISO_8859_1 = "ISO-8859-1";

	/** 8 位 UCS 转换格式 */
	public static final String UTF_8 = "UTF-8";

	/** 16 位 UCS 转换格式，Big Endian（最低地址存放高位字节）字节顺序 */
	public static final String UTF_16BE = "UTF-16BE";

	/** 16 位 UCS 转换格式，Little-endian（最高地址存放低位字节）字节顺序 */
	public static final String UTF_16LE = "UTF-16LE";

	/** 16 位 UCS 转换格式，字节顺序由可选的字节顺序标记来标识 */
	public static final String UTF_16 = "UTF-16";

	/** 中文超大字符集 */
	public static final String GBK = "GBK";

	/** 判断中文的正则表达式 */
	public static final String CHINESE_CHAR_REG_STR = "[\\u4E00-\\u9FA5]+";

	public final static String[] CHINESE_NUMBERS = { "零", "一", "二", "三", "四",
			"五", "六", "七", "八", "九" };
	public final static String[] CHINESE_NUMBER_UNITS = { "", "十", "百", "千",
			"万", "十万", "百万", "千万", "亿" };

	/**
	 * 将字符编码转换成US-ASCII码
	 */
	public String toASCII(String str) throws UnsupportedEncodingException {
		return this.changeCharset(str, US_ASCII);
	}

	/**
	 * 将字符编码转换成ISO-8859-1码
	 */
	public String toISO_8859_1(String str) throws UnsupportedEncodingException {
		return this.changeCharset(str, ISO_8859_1);
	}

	/**
	 * 将字符编码转换成UTF-8码
	 */
	public String toUTF_8(String str) throws UnsupportedEncodingException {
		return this.changeCharset(str, UTF_8);
	}

	/**
	 * 将字符编码转换成UTF-16BE码
	 */
	public String toUTF_16BE(String str) throws UnsupportedEncodingException {
		return this.changeCharset(str, UTF_16BE);
	}

	/**
	 * 将字符编码转换成UTF-16LE码
	 */
	public String toUTF_16LE(String str) throws UnsupportedEncodingException {
		return this.changeCharset(str, UTF_16LE);
	}

	/**
	 * 将字符编码转换成UTF-16码
	 */
	public String toUTF_16(String str) throws UnsupportedEncodingException {
		return this.changeCharset(str, UTF_16);
	}

	/**
	 * 将字符编码转换成GBK码
	 */
	public String toGBK(String str) throws UnsupportedEncodingException {
		return this.changeCharset(str, GBK);
	}

	/**
	 * 字符串编码转换的实现方法
	 * 
	 * @param str
	 *            待转换编码的字符串
	 * @param newCharset
	 *            目标编码
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public String changeCharset(String str, String newCharset)
			throws UnsupportedEncodingException {
		if (str != null) {
			// 用默认字符编码解码字符串。
			byte[] bs = str.getBytes();
			// 用新的字符编码生成字符串
			return new String(bs, newCharset);
		}
		return null;
	}

	/**
	 * 字符串编码转换的实现方法
	 * 
	 * @param str
	 *            待转换编码的字符串
	 * @param oldCharset
	 *            原编码
	 * @param newCharset
	 *            目标编码
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public String changeCharset(String str, String oldCharset, String newCharset)
			throws UnsupportedEncodingException {
		if (str != null) {
			// 用旧的字符编码解码字符串。解码可能会出现异常。
			byte[] bs = str.getBytes(oldCharset);
			// 用新的字符编码生成字符串
			return new String(bs, newCharset);
		}
		return null;
	}

	/**
	 * 判断文件的编码类型，参考开源项目 http://cpdetector.sourceforge.net/
	 * 
	 * @param filePath
	 *            文件所在路径
	 * @return
	 */
	public String getFileCharacterEncoding(File file) {

		CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
		/*------------------------------------------------------------------------  
		
		detector是探测器，它把探测任务交给具体的探测实现类的实例完成。  

		cpDetector内置了一些常用的探测实现类，这些探测实现类的实例可以通过add方法  

		加进来，如ParsingDetector、 JChardetFacade、ASCIIDetector、UnicodeDetector。    

		detector按照“谁最先返回非空的探测结果，就以该结果为准”的原则返回探测到的  

		字符集编码。 
		------------------------------------------------------------------------ */

		detector.add(JChardetFacade.getInstance());

		detector.add(new ByteOrderMarkDetector());

		// A simple detector that may be used to detect plain ASCII(即可以判断是否是
		// GB2312)
		detector.add(ASCIIDetector.getInstance());
		// UnicodeDetector用于Unicode家族编码的测定
		detector.add(UnicodeDetector.getInstance());

		Charset charset = null;

		try {
			charset = detector.detectCodepage(file.toURL());
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (charset == null)
			return null;

		return charset.name();

	}

	/**
	 * 为文件重新编码。仅对文本文件进行编码。为了防止错误，应该对拷贝进行编码，不宜直接在源文件上面直接操作。
	 * 
	 * @param srcFile
	 *            源文件（可以是文件夹）
	 * @param fileEncoding
	 *            指定的编码格式
	 */

	// 该方法还不成熟，utf-8 编码变成 asc 时会乱码。
	// gb2312 到 utf-8 不出现乱码

	// 所以该方法还不能实现在 "windows 的文本编辑器"中，选择编码之后，文本“另存”的功能

	public void changeFileCharacterEncoding(File srcFile, String fileEncoding) {

		// 待编码的文本文件类型
		String[] extension = { "java", "html", "htm", "php", "ini", "bat",
				"css", "txt", "js", "jsp", "xml", "sql", "properties" };
		try {
			if (srcFile.isFile()) {
				if (FilenameUtils.isExtension(srcFile.getName().toLowerCase(),
						extension)) {
					String encodingSrc = this.getFileCharacterEncoding(srcFile);
					System.out.print("encode file from '" + encodingSrc
							+ "' to '" + fileEncoding + "' : "
							+ srcFile.getAbsolutePath());
					// 先是读操作，之后是写操作，所以在同一个文件上面进行先读后写是可以的。
					// 读之前先判断源文件的编码格式。
					FileUtils.writeLines(srcFile, fileEncoding,
							FileUtils.readLines(srcFile, encodingSrc));
					System.out.println(",  finished.");
				}
			} else if (srcFile.isDirectory()) {
				File fs[] = srcFile.listFiles();
				for (File f : fs)
					changeFileCharacterEncoding(f, fileEncoding);
			} else {
				System.out.println("wrong file type :"
						+ srcFile.getAbsolutePath());
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 将小于1万万亿的整数表达为中文，传统中文标准数字单位为：个，十， 佰，仟，万，亿，兆，京，后面四个都是以万为进的单位，但兆和京在现
	 * 代很少使用，故大于亿均以万亿为单位。
	 * 
	 * @param n
	 *            小于1万万亿的整数
	 * @return 对应的中文习惯表达的数值字符串串。
	 */
	public static String convertNumToChinese(long n) {
		if (n >= (long) 1e+8 * (long) 1e+8) {
			return "";
		}

		if (n == 0) {
			return CHINESE_NUMBERS[0];
		}

		StringBuffer sb = new StringBuffer();

		int yi = 0, wan = 0, qian = 0, wanYi = 0;

		if (n >= 100000000) {
			yi = (int) (n / 100000000);
			wan = (int) ((n - (long) yi * 100000000) / 10000);
			qian = (int) (n % 10000);
		} else if (n >= 10000) {
			wan = (int) (n / 10000);
			qian = (int) (n % 10000);
		} else if (n >= 0) {
			qian = (int) n;
		}

		if (yi != 0) {
			wanYi = yi / 10000;
			yi = yi - wanYi * 10000;
			if (wanYi > 0) {
				sb.append(cunvertNumForLessThanTenThousand(wanYi)).append(
						CHINESE_NUMBER_UNITS[4]);
			}
			if (yi < 1000) {
				sb.append(CHINESE_NUMBERS[0]);
			}
			sb.append(cunvertNumForLessThanTenThousand(yi)).append(
					CHINESE_NUMBER_UNITS[8]);
		}

		if ((yi != 0 && wan < 1000 && wan > 0) || (wan == 0 && yi != 0)) {
			sb.append(CHINESE_NUMBERS[0]);
		}
		if (wan > 0) {
			sb.append(cunvertNumForLessThanTenThousand(wan)).append(
					CHINESE_NUMBER_UNITS[4]);
		}

		if (wan != 0 && qian < 1000 && qian > 0) {
			sb.append(CHINESE_NUMBERS[0]);
		}
		sb.append(cunvertNumForLessThanTenThousand(qian)).append(
				CHINESE_NUMBER_UNITS[0]);

		return sb.toString();
	}

	/**
	 * 将小于1万的整数为以中文习惯来表达.
	 * 
	 * @param n
	 *            小于1万的整数
	 * @return 对应的中文习惯表达的数值字符串
	 */
	private static String cunvertNumForLessThanTenThousand(int n) {
		if (n < 0 || n > 9999) {
			return null;
		}

		if (n == 0) {
			return "";
		}

		StringBuffer sb = new StringBuffer();

		int ge = 0, shi = 0, bai = 0, qian = 0;

		// wan = n / 10000;
		qian = n / 1000;
		bai = (n - qian * 1000) / 100;
		shi = (n - qian * 1000 - bai * 100) / 10;
		ge = n % 10;

		// if (wan != 0) {
		// sb.append(CHINESE_NUMBERS[wan]).append(CHINESE_NUMBER_UNITS[4]);
		// }

		if (qian != 0) {
			sb.append(CHINESE_NUMBERS[qian]).append(CHINESE_NUMBER_UNITS[3]);
		}

		if (bai != 0) {
			sb.append(CHINESE_NUMBERS[bai]).append(CHINESE_NUMBER_UNITS[2]);
		} else if (qian != 0 && !(shi == 0 && ge == 0)) {
			sb.append(CHINESE_NUMBERS[0]);
		}

		if (shi != 0) {
			sb.append(CHINESE_NUMBERS[shi]).append(CHINESE_NUMBER_UNITS[1]);
		} else if (bai != 0 && ge != 0) {
			sb.append(CHINESE_NUMBERS[0]);
		}

		if (ge != 0) {
			sb.append(CHINESE_NUMBERS[ge]);
		}

		return sb.toString();
	}

	/**
	 * 阿拉伯数据格式金额转中文大写金额。基于他人代码改进而来，原代码存在浮点精度bug，原作者未知。
	 * 
	 * @param value
	 * @return
	 */
	public static String numMonyToZhBig(BigDecimal value) {
		char[] hunit = { '拾', '佰', '仟' };// 段内位置表示
		char[] vunit = { '万', '亿' };// 段名表示
		char[] digit = { '零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖' };// 数字表示
		value = value.setScale(2, RoundingMode.HALF_UP);
		String valStr = value.multiply(new BigDecimal(100))
				.setScale(0, RoundingMode.HALF_UP).toString();// 转化成字符串
		String head = valStr.substring(0, valStr.length() - 2);// 取整数部分
		String rail = valStr.substring(valStr.length() - 2);// 取小数部分

		String prefix = "";// 整数部分转化的结果
		String suffix = "";// 小数部分转化的结果
		// 处理小数点后面的数
		if (rail.equals("00")) {// 如果小数部分为0
			suffix = "整";
		} else {
			suffix = digit[rail.charAt(0) - '0'] + "角"
					+ digit[rail.charAt(1) - '0'] + "分";// 否则把角分转化出来
		}
		// 处理小数点前面的数
		char[] chDig = head.toCharArray();// 把整数部分转化成字符数组
		char zero = '0';// 标志'0'表示出现过0
		byte zeroSerNum = 0;// 连续出现0的次数
		for (int i = 0; i < chDig.length; i++) {// 循环处理每个数字
			int idx = (chDig.length - i - 1) % 4;// 取段内位置
			int vidx = (chDig.length - i - 1) / 4;// 取段位置
			if (chDig[i] == '0') {// 如果当前字符是0
				zeroSerNum++;// 连续0次数递增
				if (zero == '0') {// 标志
					zero = digit[0];
				} else if (idx == 0 && vidx > 0 && zeroSerNum < 4) {
					prefix += vunit[vidx - 1];
					zero = '0';
				}
				continue;
			}
			zeroSerNum = 0;// 连续0次数清零
			if (zero != '0') {// 如果标志不为0,则加上,例如万,亿什么的
				prefix += zero;
				zero = '0';
			}
			prefix += digit[chDig[i] - '0'];// 转化该数字表示
			if (idx > 0)
				prefix += hunit[idx - 1];
			if (idx == 0 && vidx > 0) {
				prefix += vunit[vidx - 1];// 段结束位置应该加上段名如万,亿
			}
		}

		if (prefix.length() > 0)
			prefix += '圆';// 如果整数部分存在,则有圆的字样
		return prefix + suffix; // 返回正确表示
	}

	/**
	 * 判断一个字符是否包含在ASCII 表中，ASCII 表中字符的整数值在0~127之间。 可以简单的认为，不在 ASCII 表中，认为是汉字。
	 * 
	 * @param value
	 * @return
	 */
	public static boolean isASCII(char value) {
		return value * 1 <= 127 && value * 1 >= 0 ? true : false;
	}

	/**
	 * 判断有几个中文字符
	 * 
	 * @param str
	 * @return
	 */
	public static int getChineseCount(String str) {
		Pattern p = Pattern.compile(CHINESE_CHAR_REG_STR);
		Matcher m = p.matcher(str);
		int count = 0;
		while (m.find()) {
			for (int i = 0; i <= m.groupCount(); i++) {
				count = count + 1;
			}
		}
		return count;
	}

	/**
	 * 去掉字符串中的中文
	 * 
	 * @param str
	 *            待处理字符串
	 * @param opposite
	 *            去除中文,true ;保留非中文,false.
	 * @return 处理结果
	 */
	public static String removeChineseString(String str, boolean opposite) {

		String resultStr = "";

		for (int i = 0; i < str.length(); i++) {
			String tempStr = org.apache.commons.lang.StringUtils.substring(str,
					i, i + 1);
			if (tempStr.matches(CHINESE_CHAR_REG_STR)) {
				if (!opposite)
					resultStr = resultStr + tempStr;
			} else if (opposite)
				resultStr = resultStr + tempStr;
		}

		return resultStr.trim();
	}

	/**
	 * 该方法未验证
	 * @param c
	 * @return
	 */
	public boolean isChineseChar(char c) {
		Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
		if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
				|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
				|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
				|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
				|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
				|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
			return true;
		}
		return false;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) throws UnsupportedEncodingException {
		// TODO Auto-generated method stub

		CharSetUtils csu = new CharSetUtils();
		// System.out.println(csu.getFileCharacterEncoding("d:\\s.txt"));
		//
		// String str = "This is a 中文的 String!";
		// System.out.println("str: " + str);
		// String gbk = csu.toGBK(str);
		// System.out.println("转换成GBK码: " + gbk);
		// System.out.println();
		// String ascii = csu.toASCII(str);
		// System.out.println("转换成US-ASCII码: " + ascii);
		// gbk = csu
		// .changeCharset(ascii, CharSetUtils.US_ASCII, CharSetUtils.GBK);
		// System.out.println("再把ASCII码的字符串转换成GBK码: " + gbk);
		// System.out.println();
		// String iso88591 = csu.toISO_8859_1(str);
		// System.out.println("转换成ISO-8859-1码: " + iso88591);
		// gbk = csu.changeCharset(iso88591, CharSetUtils.ISO_8859_1,
		// CharSetUtils.GBK);
		// System.out.println("再把ISO-8859-1码的字符串转换成GBK码: " + gbk);
		// System.out.println();
		// String utf8 = csu.toUTF_8(str);
		// System.out.println("转换成UTF-8码: " + utf8);
		// gbk = csu.changeCharset(utf8, CharSetUtils.UTF_8, CharSetUtils.GBK);
		// System.out.println("再把UTF-8码的字符串转换成GBK码: " + gbk);
		// System.out.println();
		// String utf16be = csu.toUTF_16BE(str);
		// System.out.println("转换成UTF-16BE码:" + utf16be);
		// gbk = csu.changeCharset(utf16be, CharSetUtils.UTF_16BE,
		// CharSetUtils.GBK);
		// System.out.println("再把UTF-16BE码的字符串转换成GBK码: " + gbk);
		// System.out.println();
		// String utf16le = csu.toUTF_16LE(str);
		// System.out.println("转换成UTF-16LE码:" + utf16le);
		// gbk = csu.changeCharset(utf16le, CharSetUtils.UTF_16LE,
		// CharSetUtils.GBK);
		// System.out.println("再把UTF-16LE码的字符串转换成GBK码: " + gbk);
		// System.out.println();
		// String utf16 = csu.toUTF_16(str);
		// System.out.println("转换成UTF-16码:" + utf16);
		// gbk = csu
		// .changeCharset(utf16, CharSetUtils.UTF_16LE, CharSetUtils.GBK);
		// System.out.println("再把UTF-16码的字符串转换成GBK码: " + gbk);
		// String s = new String("中文".getBytes("UTF-8"), "UTF-8");
		// System.out.println(s);

		// csu.changeFileCharacterEncoding(new File(
		// "C:\\Documents and Settings\\jianghui\\桌面\\spring\\18"),
		// csu.UTF_8);

		String sss = "查看文件编码abc[\\u4E00-\\u9FA5]+";

		System.out.println(csu.removeChineseString(sss, true));

		System.out.println(csu.removeChineseString(sss, false));

		// System.out.println(csu.getFileCharacterEncoding(new
		// File("d:\\3.txt")));

	}

}
